package application

import (
	"context"
	log "github.com/sirupsen/logrus"
	"usbipproxy/domain"
	"usbipproxy/domain/service"
	"usbipproxy/domain/usbipserversrv"
	"usbipproxy/pb"
)

type TcpMsgUsbIpServerHandleServiceImpl struct {
	msgHandle    map[pb.MsgType]pbMsgHandler
	ctx          context.Context
	msgNotifySvc service.MsgNotifyService
	usbipCliMgr  usbipserversrv.UsbipServerSrvMgrService
	usbIpCmdSvc  service.UsbIpServerCmdService
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) Handle(msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	if msg == nil {
		return nil
	}
	if msg.GetMsgType() != pb.MsgType_TO_SERVER_ATTACH_DATA {
		log.Debugf("TcpMsgUsbIpServerHandleServiceImpl Handle msg:%+v", msg)
	} else {
		log.Debugf("TcpMsgUsbIpServerHandleServiceImpl Handle msgType:%v len(data):%v",
			msg.GetMsgType(), len(msg.GetToServerAttachDataParam().GetData()))
	}
	handleFun := impl.msgHandle[msg.MsgType]
	if handleFun != nil {
		handleFun(impl.ctx, msg, userCli)
	}

	return nil
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) reg(msgType pb.MsgType, fun pbMsgHandler) {
	impl.msgHandle[msgType] = fun
}

func NewTcpMsgTermHandleService(msgNotifySvc service.MsgNotifyService,
	usbipCliMgr usbipserversrv.UsbipServerSrvMgrService, usbIpCmdSvc service.UsbIpServerCmdService) TcpMsgHandleService {
	impl := &TcpMsgUsbIpServerHandleServiceImpl{
		msgHandle:    make(map[pb.MsgType]pbMsgHandler),
		ctx:          context.Background(),
		msgNotifySvc: msgNotifySvc,
		usbipCliMgr:  usbipCliMgr,
		usbIpCmdSvc:  usbIpCmdSvc,
	}

	impl.reg(pb.MsgType_TO_SERVER_ATTACH, impl.handleServerBeAttached)
	impl.reg(pb.MsgType_TO_SERVER_ATTACH_DATA, impl.handleToServerAttachData)
	impl.reg(pb.MsgType_TO_SERVER_ATTACH_OVER, impl.handleServerBeAttachedOver)
	impl.reg(pb.MsgType_TO_SERVER_DETACH, impl.handleServerDetach)
	impl.reg(pb.MsgType_HEARTBEAT, impl.handleServerHeartNotify)

	return impl
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) handleServerHeartNotify(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetHeartbeatParam().GetBusId()
	sessId := msg.GetHeartbeatParam().GetSessId()
	log.Infof("TcpMsgUsbIpServerHandleServiceImpl handleServerHeartNotify sessId:%v busId:%v begin", sessId, busId)
	/*
		heartMsg := &pb.ProxyMsg{
			MsgType: pb.MsgType_CLIENT_HEARTBEAT,
			Base:    &pb.BaseParam{},
			Param:   &pb.ProxyMsg_ClientHeartbeatParam{ClientHeartbeatParam: &pb.ClientHeartbeatParam{BusId: busId, SessId: sessId}},
		}
		impl.msgNotifySvc.SendMsg(heartMsg)
	*/
	impl.usbipCliMgr.RecvHeartbeatMsg(busId, sessId)
	return nil
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) handleServerBeAttached(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetToServerAttachParam().GetBusId()
	sessId := msg.GetToServerAttachParam().GetSessId()
	vendorId := msg.GetToServerAttachParam().GetVendorId()
	log.Infof("TcpMsgUsbIpServerHandleServiceImpl handleServerBeAttached sessId:%v busId:%v vendorId:%v notifyConnId:%v begin",
		sessId, busId, vendorId, msg.GetBase().GetToId())

	//impl.usbipCliMgr.Remove(busId, "")
	//time.Sleep(time.Second * 2)

	usbipCli := usbipserversrv.NewUsbipServerSrvServiceImpl("127.0.0.1:3240",
		busId, vendorId, sessId, msg.GetBase().GetToId(), impl.msgNotifySvc)
	impl.usbipCliMgr.Add(busId, sessId, usbipCli)

	go func() {
		//busId := msg.GetServerBeAttachedParam().GetBusId()
		defer func() {
			impl.usbipCliMgr.Remove(busId, sessId)
		}()
		err := usbipCli.Start()
		if err != nil {
			log.Errorf("TcpMsgUsbIpServerHandleServiceImpl handleServerBeAttached connect tcp server 127.0.0.1:3240 err: %v", err)
			return
		}
		log.Infof("TcpMsgUsbIpServerHandleServiceImpl handleServerBeAttached sessId:%v busId:%v over", sessId, busId)
	}()
	return nil
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) handleToServerAttachData(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	sessId := msg.GetToServerAttachDataParam().GetSessId()
	busId := msg.GetToServerAttachDataParam().GetBusId()
	data := msg.GetToServerAttachDataParam().GetData()
	log.Debugf("handleToServerAttachData busId:%v sessId:%v len(data):%v", busId, sessId, len(data))
	impl.usbipCliMgr.RecvMsg(busId, sessId, data)
	return nil
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) handleServerBeAttachedOver(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetToServerAttachOverParam().GetBusId()
	sessId := msg.GetToServerAttachOverParam().GetSessId()
	log.Infof("handleServerBeAttachedOver sessId:%v busId:%v toId:%v", sessId, busId, msg.GetBase().GetToId())
	busList := impl.usbipCliMgr.GetBusIdsByConnId(msg.GetBase().GetToId())
	if busId == "all" {
		for _, item := range busList {
			//if item.SessId == sessId {
			log.Infof("handleServerDetach sessId:%v busId:%v unbind and remove", sessId, busId)
			impl.usbIpCmdSvc.UnBind(item.BusId)
			impl.usbipCliMgr.Remove(item.BusId, item.SessId)
			//break
			//}
		}
	} else {
		for _, item := range busList {
			if item.SessId == sessId {
				log.Infof("handleServerDetach sessId:%v busId:%v unbind and remove", sessId, busId)
				impl.usbIpCmdSvc.UnBind(busId)
				impl.usbipCliMgr.Remove(busId, sessId)
				break
			}
		}
	}
	log.Infof("handleServerBeAttachedOver sessId:%v busId:%v connId:%v over", sessId, busId, msg.GetBase().GetToId())

	return nil
}

func (impl *TcpMsgUsbIpServerHandleServiceImpl) handleServerDetach(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetToServerDetachParam().GetBusId()
	sessId := msg.GetToServerDetachParam().GetSessId()
	vendorId := msg.GetToServerDetachParam().GetVendorId()
	connId := msg.GetBase().GetToId()
	log.Infof("handleServerDetach sessId:%v busId:%v connId:%v vendorId:%v begin", sessId, busId, connId, vendorId)
	//impl.usbipCliMgr.Remove(busId, sessId)

	busList := impl.usbipCliMgr.GetBusIdsByConnId(connId)
	for _, item := range busList {
		if item.SessId == sessId {
			log.Infof("handleServerDetach sessId:%v busId:%v connId:%v vendorId:%v hit", sessId, busId, connId, vendorId)
			impl.usbIpCmdSvc.UnBind(busId)
			impl.usbipCliMgr.Remove(busId, sessId)
			break
		}
	}
	log.Infof("handleServerDetach sessId:%v busId:%v connId:%v over", sessId, busId, msg.GetBase().GetToId())
	return nil
}
